package navigation;

import java.util.*;
import config.*;

/**
 * Class for pathfinding
 *
 * @author hyurah
 */

public class Wayfinder {

	/**
	 * Find the shortest known path to the search-target
	 *
	 * @param searchTarget wanted target
	 * @param map known map
	 * @param xPosition x start position in map
	 * @param yPosition y start position in map
	 * @return path-queue with waypoints to searched target
	 * @throws IllegalMapStateException when search-target is not reachable in known map
	 */
	public static PathQueue findShortestPath(SearchTarget searchTarget, Map map, int xPosition, int yPosition) throws IllegalMapStateException {
		int counter = 0;
		List<PathQueue> list = new ArrayList<PathQueue>();
		List<PathQueue> newlist = new ArrayList<PathQueue>();
		List<PathQueue> resultlist = new ArrayList<PathQueue>();
		PathQueue result = new PathQueue();
		boolean searching = true;
		boolean followDirection;
		result.pushPoint(new PathPoint(xPosition, yPosition));
		list.add(result);
		while(searching) {
			for(int i=0; i<list.size(); i++) {
				for(int j=0; j<4; j++) {
					followDirection = false;
					if(list.get(i).size()==1) {
						followDirection = true;
					} else if((list.get(i).getPoint(list.get(i).size()-2).getDirection()+2)%4!=j) {
						followDirection = true;
					}
					if(followDirection) {
						if(map.getFieldDirectionState(list.get(i).getLast().getX(), list.get(i).getLast().getY(), j)==FieldDirectionState.PATH) {
							result = list.get(i).clone();
							result.getLast().setDirection(j);
							switch(j) {
								case Direction.NORTH:
									result.pushPoint(new PathPoint(result.getLast().getX(), result.getLast().getY()-1));
									break;
								case Direction.EAST:
									result.pushPoint(new PathPoint(result.getLast().getX()+1, result.getLast().getY()));
									break;
								case Direction.SOUTH:
									result.pushPoint(new PathPoint(result.getLast().getX(), result.getLast().getY()+1));
									break;
								case Direction.WEST:
									result.pushPoint(new PathPoint(result.getLast().getX()-1, result.getLast().getY()));
									break;
							}
							newlist.add(result);
							switch(searchTarget) {
								case EXIT:
									if(map.fieldIsExit(result.getLast().getX(), result.getLast().getY())) {
										searching = false;
										resultlist.add(result);
									}
									break;
								case REDPOINT:
									if(map.fieldIsRedPoint(result.getLast().getX(), result.getLast().getY())) {
										searching = false;
										resultlist.add(result);
									}
									break;
								case UNKNOWNFIELD:
									if(!map.fieldIsKnown(result.getLast().getX(), result.getLast().getY())) {
										searching = false;
										resultlist.add(result);
									}
									break;
								case START:
									if(!map.fieldIsKnown(result.getLast().getX(), result.getLast().getY())) {
										searching = false;
										resultlist.add(result);
									}
									break;
							}
						}
					}
				}
			}
			list = newlist;
			newlist = new ArrayList<PathQueue>();
			counter++;
			if(list.size()==0 || counter>Config.MAXFIELDS*Config.MINFIELDS) {
				switch(searchTarget) {
					case EXIT: throw new IllegalMapStateException("No Exit detectable (Map corrupt)");
					case REDPOINT: throw new IllegalMapStateException("RedPoint not detectable (RedPoint-Location unknown)");
					case UNKNOWNFIELD: throw new IllegalMapStateException("No UnknownField detectable (whole Map known)");
				}
			}
		}
		result = resultlist.get(0);
		for(int i=1; i<resultlist.size(); i++) {
			if(resultlist.get(i).getLast().getY()>result.getLast().getY());
				result = resultlist.get(i);
		}
		return result;
	}

}
